package tech.xs.workflow.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.xs.common.domain.enums.YesNoEnum;
import tech.xs.common.framework.service.CrudServiceImpl;
import tech.xs.common.framework.util.Assert;
import tech.xs.common.framework.util.AuthUtil;
import tech.xs.common.framework.util.SysUserUtil;
import tech.xs.common.sys.domain.bo.CurrLoginUserBo;
import tech.xs.common.sys.domain.bo.UserDetailsBo;
import tech.xs.workflow.dao.*;
import tech.xs.workflow.domain.entity.*;
import tech.xs.workflow.domain.enums.BpmnEventTypeEnum;
import tech.xs.workflow.domain.exception.BpmnException;
import tech.xs.workflow.service.BpmnEventService;
import tech.xs.workflow.service.WorkflowWorkService;

import javax.annotation.Resource;
import java.util.List;

@Slf4j
@Service
public class WorkflowWorkServiceImpl extends CrudServiceImpl<WorkflowWorkDao, WorkflowWork> implements WorkflowWorkService {

    @Resource
    private WorkflowDefineDao workflowDefineDao;
    @Resource
    private WorkflowWorkDao workflowWorkDao;
    @Resource
    private BpmnEventService bpmnEventService;
    @Resource
    private BpmnSequenceFlowDao bpmnSequenceFlowDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkflowWork startWorkflow(String code) {
        WorkflowDefine workflowDefine = workflowDefineDao.selectOne(Wrappers.<WorkflowDefine>lambdaQuery()
                .eq(WorkflowDefine::getStatus, YesNoEnum.YES)
                .eq(WorkflowDefine::getCode, code));
        Assert.isNotNull(workflowDefine, "流程定义不存在或不存在启用流程定义:" + code);
        WorkflowWork workflowWork = new WorkflowWork();
        workflowWork.setDefineId(workflowDefine.getId());
        workflowWork.setName(workflowDefine.getName());
        workflowWork.setBpmnId(workflowDefine.getBpmnId());
        CurrLoginUserBo currLoginUser = AuthUtil.getCurrLoginUser();
        if (currLoginUser != null) {
            Long userId = currLoginUser.getUserId();
            workflowWork.setInitiatorId(userId);
            workflowWork.setInitiatorUserName(currLoginUser.getUserName());
            UserDetailsBo userDetails = SysUserUtil.getUserDetails(userId);
            if (userDetails != null) {
                workflowWork.setInitiatorRealName(userDetails.getRealName());
            }
        }
        workflowWorkDao.insert(workflowWork);

        List<BpmnEvent> startEventList = bpmnEventService.list(Wrappers.<BpmnEvent>lambdaQuery()
                .eq(BpmnEvent::getEventType, BpmnEventTypeEnum.START_EVENT)
                .eq(BpmnEvent::getProcessId, workflowDefine.getBpmnId()));
        if (startEventList.isEmpty()) {
            throw new BpmnException("流程不存在开始节点:" + workflowWork);
        }
        if (startEventList.size() > 1) {
            throw new BpmnException("流程存在多个开始节点:" + workflowWork);
        }
        executeNode(workflowWork, startEventList.get(0));
        return workflowWork;
    }

    @Override
    public boolean executeNode(WorkflowWork work, BpmnNode node) {
        log.info("执行流程节点-开始 work:" + work + " node:" + node);
        boolean isNext = false;
        if (node instanceof BpmnEvent) {
            isNext = bpmnEventService.execute(work, (BpmnEvent) node);
        } else {
            throw new BpmnException("未知节点类型,无法处理:" + node);
        }
        log.info("执行流程节点-结束 work:" + work + " node:" + node);
        if (isNext) {
            return executeNextNode(work, node);
        }
        return false;
    }

    @Override
    public boolean executeNextNode(WorkflowWork work, BpmnNode event) {
        BpmnNode nextNode = getNextNode(work.getBpmnId(), event.getNodeId());
        if (nextNode == null) {
            return false;
        }
        return executeNode(work, nextNode);
    }

    private BpmnNode getNextNode(Long bpmnId, String nodeId) {
        List<BpmnSequenceFlow> sequenceFlowList = bpmnSequenceFlowDao.selectList(Wrappers.<BpmnSequenceFlow>lambdaQuery()
                .eq(BpmnSequenceFlow::getProcessId, bpmnId)
                .eq(BpmnSequenceFlow::getSourceRef, nodeId)
        );
        if (sequenceFlowList.isEmpty()) {
            log.info("未找到下一节点 bpmnId:" + bpmnId + " nodeId:" + nodeId);
            return null;
        }
        BpmnSequenceFlow bpmnSequenceFlow = sequenceFlowList.get(0);
        switch (bpmnSequenceFlow.getTargetNodeType()) {
            case EVENT: {
                return bpmnEventService.getOne(Wrappers.<BpmnEvent>lambdaQuery().eq(BpmnNode::getProcessId, bpmnId).eq(BpmnNode::getNodeId, bpmnSequenceFlow.getTargetRef()));
            }
            default: {
                throw new BpmnException("查找目标节点失败:" + bpmnSequenceFlow);
            }
        }
    }

}
